//
// Created by Leaf on 2025/8/18.
//

#include "ledMatrix.h"

#if defined(ESP32) || defined(ESP8266)
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#endif

/// 构造函数，初始化引脚和设备数量，并分配状态内存
LedMatrix::LedMatrix(uint8_t dataPin, uint8_t clkPin, uint8_t csPin, uint8_t numDevices) {
    this->dinPin = dataPin;
    this->clkPin = clkPin;
    this->csPin = csPin;
    this->numDevices = numDevices;
    
    // 初始化状态数组
    this->state = new uint8_t*[numDevices];
    for (uint8_t i = 0; i < numDevices; i++) {
        this->state[i] = new uint8_t[8];
        for (uint8_t j = 0; j < 8; j++) {
            this->state[i][j] = 0;
        }
    }
}

/// 初始化点阵屏，设置引脚模式和MAX7219寄存器
void LedMatrix::begin() {
    pinMode(dinPin, OUTPUT);
    pinMode(clkPin, OUTPUT);
    pinMode(csPin, OUTPUT);
    digitalWrite(csPin, HIGH);
    
    // 初始化MAX7219
    for (uint8_t i = 0; i < numDevices; i++) {
        transfer(MAX7219_REG_SCAN_LIMIT, 0x07, i);       // 扫描限制为整个矩阵(0-7)
        transfer(MAX7219_REG_DECODE_MODE, 0x00, i);      // 使用LED矩阵模式(不使用7段译码)
        transfer(MAX7219_REG_INTENSITY, 0x0f, i);        // 设置亮度为最大值
        transfer(MAX7219_REG_SHUTDOWN, 0x01, i);         // 启用显示
        transfer(MAX7219_REG_DISPLAY_TEST, 0x00, i);     // 正常操作模式(非测试模式)
    }
    
    clear();
    update();
}

/// 向指定设备发送命令和数据
void LedMatrix::transfer(uint8_t opcode, uint8_t data, uint8_t device) {
    if (device >= numDevices) return;
    spiTransfer(opcode, data, device);
}

/// 通过SPI接口传输数据到指定设备
void LedMatrix::spiTransfer(uint8_t opcode, uint8_t data, uint8_t device) const {
    // 选择设备
    digitalWrite(csPin, LOW);

    // 发送数据到所有设备
    for (uint8_t i = 0; i < numDevices; i++) {
        if (i == device) {
            shiftOut(dinPin, clkPin, MSBFIRST, opcode);
            shiftOut(dinPin, clkPin, MSBFIRST, data);
        } else {
            // 对于其他设备发送空操作
            shiftOut(dinPin, clkPin, MSBFIRST, MAX7219_REG_NOOP);
            shiftOut(dinPin, clkPin, MSBFIRST, 0);
        }
    }

    // 取消选择设备
    digitalWrite(csPin, HIGH);
}

/// 设置点阵屏亮度（0-15级）
void LedMatrix::setIntensity(uint8_t intensity) {
    if (intensity > 15) intensity = 15;
    for (uint8_t i = 0; i < numDevices; i++) {
        transfer(MAX7219_REG_INTENSITY, intensity, i);
    }
}

/// 清空点阵屏显示内容
void LedMatrix::clear() {
    for (uint8_t device = 0; device < numDevices; device++) {
        for (uint8_t i = 0; i < 8; i++) {
            state[device][i] = 0;
        }
    }
}

/// 设置指定位置LED的状态
void LedMatrix::setLed(uint8_t row, uint8_t col, bool state) {
    if (row > 7 || col > 7 * numDevices) return;
    
    uint8_t device = col / 8;
    uint8_t col_in_device = col % 8;
    
    if (device >= numDevices) return;
    
    // 更新内部状态
    if (state) {
        this->state[device][row] |= 1 << col_in_device;
    } else {
        this->state[device][row] &= ~(1 << col_in_device);
    }
}

/// 设置整行的显示内容
void LedMatrix::setRow(uint8_t row, uint8_t value) {
    if (row > 7) return;
    
    for (uint8_t device = 0; device < numDevices; device++) {
        state[device][row] = value;
    }
}

/// 设置整列的显示内容
void LedMatrix::setColumn(uint8_t col, uint8_t value) {
    if (col > 7 * numDevices) return;
    
    uint8_t device = col / 8;
    uint8_t col_in_device = col % 8;
    
    if (device >= numDevices) return;
    
    for (uint8_t row = 0; row < 8; row++) {
        if (value & (1 << row)) {
            state[device][row] |= 1 << col_in_device;
        } else {
            state[device][row] &= ~(1 << col_in_device);
        }
    }
}

/// 在指定位置绘制精灵图/图案
void LedMatrix::drawSprite(int8_t x, int8_t y, const uint8_t sprite[], uint8_t width, uint8_t height) {
    for (uint8_t iy = 0; iy < height; iy++) {
        for (uint8_t ix = 0; ix < width; ix++) {
            if (sprite[iy] & (1 << (width - 1 - ix))) {
                // 检查坐标是否在有效范围内
                if ((y + iy) >= 0 && (y + iy) < 8 && (x + ix) >= 0 && (x + ix) < (8 * numDevices)) {
                    setLed(y + iy, x + ix, true);
                }
            }
        }
    }
}

/// 更新显示内容，将缓冲区数据发送到点阵屏
void LedMatrix::update() {
    for (uint8_t device = 0; device < numDevices; device++) {
        // 只发送有效的行寄存器(0x01-0x08)
        for (uint8_t row = 0; row < 8; row++) {
            // 确保寄存器地址在有效范围内
            uint8_t reg = row + 1;
            if (reg >= 0x01 && reg <= 0x08) {
                transfer(reg, state[device][row], device);
            }
        }
    }
}

/**
 * 绘制旋转指定角度的精灵图/图案
 * @param x 显示位置的x坐标
 * @param y 显示位置的y坐标
 * @param sprite 要显示的图案数组
 * @param width 图案宽度
 * @param height 图案高度
 * @param angle 旋转角度（仅支持0、90、180、270度的倍数）
 */
void LedMatrix::drawSpriteRotated(int8_t x, int8_t y, const uint8_t sprite[], uint8_t width, uint8_t height, uint16_t angle) {
    // 角度标准化到0-360范围内
    angle = angle % 360;
    
    // 只支持90度的倍数旋转
    if (angle % 90 != 0) {
        return;
    }
    
    // 如果是0度，则直接绘制
    if (angle == 0) {
        drawSprite(x, y, sprite, width, height);
        return;
    }
    
    // 创建旋转后的图案缓冲区
    uint8_t rotatedSprite[8]; // 最大支持8x8图案
    uint8_t rotatedWidth, rotatedHeight;
    
    // 计算旋转后的尺寸
    if (angle == 90 || angle == 270) {
        rotatedWidth = height;
        rotatedHeight = width;
    } else { // 180度
        rotatedWidth = width;
        rotatedHeight = height;
    }
    
    // 初始化旋转后的图案
    for (uint8_t i = 0; i < 8; i++) {
        rotatedSprite[i] = 0;
    }
    
    // 根据角度进行旋转
    switch (angle) {
        case 90: // 顺时针旋转90度
            for (uint8_t row = 0; row < height; row++) {
                for (uint8_t col = 0; col < width; col++) {
                    if (sprite[row] & (1 << (width - 1 - col))) {
                        rotatedSprite[col] |= 1 << row;
                    }
                }
            }
            break;
            
        case 180: // 旋转180度
            for (uint8_t row = 0; row < height; row++) {
                for (uint8_t col = 0; col < width; col++) {
                    if (sprite[row] & (1 << (width - 1 - col))) {
                        rotatedSprite[height - 1 - row] |= 1 << col;
                    }
                }
            }
            break;
            
        case 270: // 顺时针旋转270度（或逆时针旋转90度）
            for (uint8_t row = 0; row < height; row++) {
                for (uint8_t col = 0; col < width; col++) {
                    if (sprite[row] & (1 << (width - 1 - col))) {
                        rotatedSprite[width - 1 - col] |= 1 << (height - 1 - row);
                    }
                }
            }
            break;
    }
    
    // 绘制旋转后的图案
    drawSprite(x, y, rotatedSprite, rotatedWidth, rotatedHeight);
}

/**
 * 绘制滚动的精灵图/图案
 * @param x 初始显示位置的x坐标
 * @param y 初始显示位置的y坐标
 * @param sprite 要显示的图案数组
 * @param width 图案宽度
 * @param height 图案高度
 * @param direction 滚动方向（0=向左，1=向右，2=向上，3=向下）
 * @param steps 滚动步数
 * @param delayMs 每步之间的延迟毫秒数
 */
void LedMatrix::drawSpriteScrolling(int8_t x, int8_t y, const uint8_t sprite[], uint8_t width, uint8_t height, 
                                   uint8_t direction, uint8_t steps, uint16_t delayMs) {
    int8_t currentX = x;
    int8_t currentY = y;
    
    for (uint8_t i = 0; i <= steps; i++) {
        clear();
        
        // 根据方向更新位置
        switch (direction) {
            case 0: // 向左滚动
                currentX = x - i;
                break;
            case 1: // 向右滚动
                currentX = x + i;
                break;
            case 2: // 向上滚动
                currentY = y - i;
                break;
            case 3: // 向下滚动
                currentY = y + i;
                break;
        }
        
        // 绘制当前帧
        drawSprite(currentX, currentY, sprite, width, height);
        update();
        
        // 延迟，自动检测是否在RTOS环境下编译
        if (delayMs > 0 && i < steps) {
#if defined(ESP32) || defined(ESP8266)
            // ESP平台使用RTOS延迟
            vTaskDelay(pdMS_TO_TICKS(delayMs));
#else
            // 其他平台使用普通delay
            delay(delayMs);
#endif
        }
    }
}

/**
 * 绘制循环滚动的精灵图/图案
 * @param x 初始显示位置的x坐标
 * @param y 初始显示位置的y坐标
 * @param sprite 要显示的图案数组
 * @param width 图案宽度
 * @param height 图案高度
 * @param direction 滚动方向（0=向左，1=向右，2=向上，3=向下）
 * @param steps 滚动步数
 * @param delayMs 每步之间的延迟毫秒数
 */
void LedMatrix::drawSpriteLoopScrolling(int8_t x, int8_t y, const uint8_t sprite[], uint8_t width, uint8_t height,
                                        uint8_t direction, uint16_t steps, uint16_t delayMs) {
    for (uint16_t i = 0; i < steps + 1; i++) {
        clear();

        int8_t offset;
        // 根据方向计算偏移量
        switch (direction) {
            case 0: // 向左滚动
                offset = i % width;
                // 绘制两部分：一部分从右侧进入，一部分向左移动
                drawSprite(x - offset, y, sprite, width, height);
                drawSprite(x - offset + width, y, sprite, width, height);
                break;
            case 1: // 向右滚动
                offset = i % width;
                // 绘制两部分：一部分从左侧进入，一部分向右移动
                drawSprite(x + offset, y, sprite, width, height);
                drawSprite(x + offset - width, y, sprite, width, height);
                break;
            case 2: // 向上滚动
                offset = i % height;
                // 绘制两部分：一部分从底部进入，一部分向上移动
                drawSprite(x, y - offset, sprite, width, height);
                drawSprite(x, y - offset + height, sprite, width, height);
                break;
            case 3: // 向下滚动
                offset = i % height;
                // 绘制两部分：一部分从顶部进入，一部分向下移动
                drawSprite(x, y + offset, sprite, width, height);
                drawSprite(x, y + offset - height, sprite, width, height);
                break;
        }

        update();

        // 延迟，自动检测是否在RTOS环境下编译
        if (delayMs > 0) {
#if defined(ESP32) || defined(ESP8266)
            // ESP平台使用RTOS延迟
            vTaskDelay(pdMS_TO_TICKS(delayMs));
#else
            // 其他平台使用普通delay
            delay(delayMs);
#endif
        }
    }
}

/**使用示例：
 * #include "hardware/ledMatrix.h"

// 创建一个点阵屏实例（使用默认引脚）
LedMatrix matrix;

// 或创建多个点阵屏级联的实例
LedMatrix matrixChain(7, 6, 5, 2); // DIN, CLK, CS, 数量

void setup() {
    matrix.begin();
    matrix.setIntensity(8); // 设置亮度

    // 点亮第一行中间的LED
    matrix.setLed(0, 3, true);
    matrix.setLed(0, 4, true);

    // 更新显示
    matrix.update();
}

void loop() {
    // 绘制一个简单的图案
    matrix.clear();
    uint8_t smiley[] = {
        0b00111100,
        0b01000010,
        0b10100101,
        0b10000001,
        0b10100101,
        0b10011001,
        0b01000010,
        0b00111100
    };
    matrix.drawSprite(0, 0, smiley, 8, 8);
    matrix.update();
    delay(1000);
    
    // 显示旋转的箭头
    matrix.clear();
    matrix.drawSpriteRotated(0, 0, ARROW_UP, 8, 8, 90);  // 旋转90度
    matrix.update();
    delay(1000);
    
    // 滚动显示箭头
    matrix.drawSpriteScrolling(0, 0, ARROW_RIGHT, 8, 8, 0, 10, 200);  // 向左滚动
}
*/